#%%
import numpy as np 
from types import FunctionType

__all__=(
    "Func",
    "sigmoid",
    "relu",
    "softmax",
)

#激活函数
class Func:
    def __init__(self,f:FunctionType,f_derivate:FunctionType,jacobin=False):
        self.f = f 
        self.f_derivate = f_derivate
        self.jacobin = jacobin # True 表明f导数将使用雅克比矩阵进行表示

    def __call__(self,z):
        return self.f(z)

    def derivate(self,z):
        return self.f_derivate(z)

# sigmomid
def f_sigmoid(z):
    return 1.0/(1.0 + np.exp(-z))

def f_sigmoid_derivate(z):
    y = f_sigmoid(z)
    return y*(1-y)

sigmoid = Func(f_sigmoid,f_sigmoid_derivate)

# relu 
def f_relu(z):
    return np.maximum(z, 0)

def f_relu_derivate(z):
    return np.heaviside(z,0.5)

relu = Func(f_relu,f_relu_derivate)

# softmax 
def f_softmax(z):
    # 直接使用np.exp(z)可能产生非常大的数以至出现nan
    # 所以分子分母同时乘以一个数来限制它
    # 这里用 exp(-np.max(z))
    exps = np.exp(z-np.max(z))
    exp_sum = np.sum(exps)
    return exps/exp_sum

def f_softmax_derivate(z):
    y = f_softmax(z).reshape((-1,))
    return np.diag(y)-y.reshape((-1,1)).dot(y.reshape(1,-1))
# softmax 导数只能用雅克比矩阵表示，无法简化
softmax = Func(f_softmax,f_softmax_derivate,True) 


# if __name__=="__main__":
#     # z = np.array([1,1,1,1])
#     # print(z*z.reshape((1,-1)).ndim)
#     # m = f_softmax(z)
#     # n = f_softmax_derivate(z)
#     # print(m.ndim,n.shape)
#     print(f_relu(np.array([-1,0,1,1,2])))
#     print(np.exp(np.array([[1,2,3]])))

